//  Copyright (C) 2014 Xiande (Triston) Cao

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef CVSIE_VECTOR3_HPP
#define CVSIE_VECTOR3_HPP

#include <complex>


#include "cvsie_datatype.hpp"

namespace cvsie {

  template <typename T>
    class CVector3;

  typedef CVector3<double> Vector3d;
  typedef CVector3<std::complex<double> > Vector3c;

  const double VECTOR3_TOLERANCE = 1.0e-16;

  template <typename T>
    class CVector3 {
      public:
        T x; // x coordinate
        T y; // y coordinate
        T z; // z coordinate
      public:
        CVector3(T _x = 0, T _y = 0, T _z = 0) : x(_x), y(_y), z(_z) {
        }

        CVector3(const CVector3<T> &p) : x(p.x), y(p.y), z(p.z) {
        }

        inline CVector3<T> & operator=(const CVector3<T> &rhs);
        
        T GetLength() const;

        inline bool operator==(const CVector3<T> &rhs) const;
        inline bool operator<(const CVector3<T> &rhs) const;

        inline CVector3<T> & operator*=(const T &scale);
        inline CVector3<T>   operator*(const T &scale) const;

        inline CVector3<T> & operator*=(const CVector3<T> &scale);
        inline CVector3<T>   operator*(const CVector3<T> &scale) const;
        inline CVector3<T>   cross(const CVector3<T> &scale) const;

        void OutputToString(std::string &str);
    };

  template <typename T>
    CVector3<T> & CVector3<T>::operator*=(const T &scale) {
      x *= scale;
      y *= scale;
      z *= scale;
      return &this;
    }

  template <typename T>
    CVector3<T> CVector3<T>::operator*(const T &scale) const {
      return CVector3<T>(x*scale, y*scale, z*scale);
    }


  template <typename T>
    CVector3<T> &CVector3<T>::operator*=(const CVector3<T> &rhs) {
      T xt = y * rhs.z - z * rhs.y;
      T yt = z * rhs.x - x * rhs.z;
      T zt = x * rhs.y - y * rhs.x;
      rhs.x = xt;
      rhs.y = yt;
      rhs.z = zt;
      return &this;
    }

  template <typename T>
    CVector3<T> CVector3<T>::operator*(const CVector3<T> &rhs) const {
      T xt = y * rhs.z - z * rhs.y;
      T yt = z * rhs.x - x * rhs.z;
      T zt = x * rhs.y - y * rhs.x;
      return CVector3<T>(xt, yt, zt);
    }
    
  template <typename T>
    CVector3<T> CVector3<T>::cross(const CVector3<T> &rhs) const {
      T xt = y * rhs.z - z * rhs.y;
      T yt = z * rhs.x - x * rhs.z;
      T zt = x * rhs.y - y * rhs.x;
      return CVector3<T>(xt, yt, zt);
    }

  template <typename T>
    CVector3<T> & CVector3<T>::operator=(const CVector3<T> &rhs) {
      if (&rhs != this) {
        x = rhs.x;      
        y = rhs.y;      
        z = rhs.z;      
      }
      return *this;
    }


  template <typename T>
    T CVector3<T>::GetLength() const {
      return static_cast<T>(sqrt(x*x + y*y + z*z));
    }


  template <typename T>
    bool CVector3<T>::operator==(const CVector3<T> &rhs) const {
      if (GetDistanceToPoint(rhs) < VECTOR3_TOLERANCE) {
        return true;
      }
      return false;
    }


  template <typename T>
    bool CVector3<T>::operator<(const CVector3<T> &rhs) const {
      if ((x < rhs.x) || (x == rhs.x && (y < rhs.y || 
              (y == rhs.y && z < rhs.z)))) { 
        return true;
      }
      return false;
    }


  template <typename T>
    void CVector3<T>::OutputToString(std::string &str) {
      //TODO: xiande.cao@gmail.com
    }

} // namespace cvsie;

#endif
